#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>
#include <sys/select.h>

static const uint16_t defaultport = 8082;
static const int backlog = 10;
static const int fd_max_num = (sizeof(fd_set) * 8);

class SelectServer
{
public:
    SelectServer(const uint16_t port = defaultport)
        : _listen_sockfd(0), _port(port)
    {
        _read_array.resize(fd_max_num, -1);
    }
    // 初始化服务器
    void Init()
    {
        // 创建监听套接字
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            std::cerr << "listensockfd socket fail" << std::endl;
            exit(-1);
        }
        std::cout << "[listensockfd socket success]" << std::endl;

        int opt = 1;
        setsockopt(_listen_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        // 绑定端口号
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        server.sin_addr.s_addr = INADDR_ANY;

        int retbind = bind(_listen_sockfd, (const struct sockaddr *)&server, sizeof(server));
        if (retbind < 0)
        {
            std::cerr << "listensockfd bind fail" << std::endl;
            exit(-1);
        }
        std::cout << "[listensockfd bind success]" << std::endl;

        // 开始监听
        int retlisten = listen(_listen_sockfd, backlog);
        if (retlisten < 0)
        {
            std::cerr << "listensockfd bind fail" << std::endl;
            exit(-1);
        }
        std::cout << "[listensockfd listen......]" << std::endl;
    }

    // 启动服务器
    void Start()
    {
        _read_array[0] = _listen_sockfd;
        while (true)
        {
            fd_set readfds;
            FD_ZERO(&readfds);

            int maxfd = _read_array[0];
            for (int i = 0; i < fd_max_num; i++)
            {
                if (_read_array[i] != -1)
                {
                    FD_SET(_read_array[i], &readfds);
                    maxfd = std::max(_read_array[i], maxfd);
                }
            }

            struct timeval timeout = {2, 0};

            int ret = select(maxfd + 1, &readfds, nullptr, nullptr, &timeout);
            if (ret < 0)
            {
                std::cerr << "select fail" << std::endl;
            }
            else if (ret == 0)
            {
                std::cout << "select time out" << std::endl;
            }
            else
            {
                // 处理事件
                HandlerEvent(readfds);
            }
        }
    }

    void HandlerEvent(fd_set &readfds)
    {
        for (int i = 0; i < fd_max_num; i++)
        {
            int fd = _read_array[i];
            if (fd == -1)
                continue;

            // fd存在
            if (FD_ISSET(fd, &readfds))
            {
                // 是监听套接字
                if (fd == _listen_sockfd)
                {
                    // 处理监听事件
                    struct sockaddr_in client;
                    memset(&client, 0, sizeof(client));
                    socklen_t len = sizeof(client);

                    //获取新连接
                    int sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len);
                    if (sockfd < 0)
                    {
                        std::cerr << "accept fail" << std::endl;
                        continue;
                    }
                    //将sockfd放入到读事件数组中
                    int pos = 1;
                    for (; pos < fd_max_num; pos++)
                    {
                        if (_read_array[pos] == -1)
                            break;
                    }
                    //数组如果满了就关闭连接
                    if (pos == fd_max_num)
                    {
                        std::cerr << "warning _read_array isfull" << std::endl;
                        close(sockfd);
                    }
                    else
                    {
                        _read_array[pos] = sockfd;
                    }
                }
                else
                {
                    // 处理读事件
                    char buffer[1024];
                    //这里的读数据没有处理粘包问题和是否为一个完整报文的问题，需要应用层协议定制
                    ssize_t n = read(fd, buffer, sizeof(buffer) - 1);  
                    if (n < 0)
                    {
                        std::cerr << "read fail" << std::endl;
                        close(fd);
                        _read_array[i] = -1;
                    }
                    else if (n == 0)
                    {
                        std::cerr << "client quit" << std::endl;
                        close(fd);
                        _read_array[i] = -1;
                    }
                    else
                    {
                        buffer[n] = '\0';
                        std::cout << "client say#" << buffer << std::endl;
                    }
                }
            }
        }
    }

private:
    int _listen_sockfd;
    uint16_t _port;
    std::vector<int> _read_array;
};